package cbbx_sm.decision_maker;

import java.util.Hashtable;
import java.util.List;
import java.util.Vector;
import java.util.Map.Entry;

import cbbx_sm.decision_maker.search.ScheduleConfigurationNode;
import cbbx_sm.decision_maker.search.SchedulerState;
import cbbx_sm.decision_maker.search.Schedules;
import cbbx_sm.decision_maker.search.States;
import cbbx_sm.decision_maker.search.TreeLookAhead;
import cbbx_sm.decision_maker.search.WorldStateTransition;
import cbbx_sm.decision_maker.search.States.NNProperty;
import cbbx_sm.probabilistic_model.Cluster;
import cbbx_sm.probabilistic_model.Prediction;
import cbbx_sm.utils.ExperimentManager;

/**
 * Based on a tree that is grown for each state the scheduler decides where to zoom.
 * The best actions are computed with a look ahead for highest expected utility.
 * 
 * @author Ronen Vaisenberg - University of California, Irvine
 *
 */
public class TreeLookAheadDecisionMaker implements IDecisionMaker {
	
	private List<String> cameraIds;
	private Hashtable<String, Cluster> clusterIndex;
	private Hashtable<String, WorldStateTransition> table;
	Hashtable<String, States> states;
	private double utilityZoom;
	private double utilityUP;
	private int numberOfTimeStampsLookAhead;
	private boolean recordNeededStates;

	
	public TreeLookAheadDecisionMaker(List<String> cameraIds,
			Hashtable<String,WorldStateTransition> table, 
			Hashtable<String, Cluster> clusterIndex,
			double utilityZoom, double utilityUP, int numberOfTimeStampsLookAhead, boolean recordNeededStates) {
		this.cameraIds = cameraIds;
		this.table = table;
		this.states = new Hashtable<String, States>();
		for (Entry<String, WorldStateTransition> e : table.entrySet()) {
			this.states.put(e.getKey(), new States(e.getValue(), false));
		}			
		

		this.clusterIndex = clusterIndex;
		this.utilityZoom = utilityZoom;
		this.utilityUP = utilityUP;
		this.numberOfTimeStampsLookAhead = numberOfTimeStampsLookAhead;
		this.recordNeededStates = recordNeededStates;
	}
	
	@Override
	public Decision makeDecision(Prediction currentPrediction) {

		List<CameraConfiguration> confs = new Vector<CameraConfiguration>();
		
		//For each camera separately compute the action based on its probabilities.
		for (String cam: cameraIds){
			double[] state = states.get(cam).get(currentPrediction.getClusterProbabilities());

			if (recordNeededStates) {
				states.get(cam).approximateProbabilities(state, ExperimentManager.approximationTechnique, true);				
			}

			WorldStateTransition wst = table.get(cam);
			ScheduleConfigurationNode root = 
					new ScheduleConfigurationNode(null, state, null,wst, 0,utilityZoom,utilityUP);
				TreeLookAhead.growTree(root,wst, numberOfTimeStampsLookAhead);
				
			String bestAction = root.getBestNextAction();			
			if (bestAction.compareTo(Schedules.UP)!=0){
				confs.add(new CameraConfiguration(cam, clusterIndex.get(bestAction)));
			}
		}
		
		Decision decision = new Decision(confs);

		return decision;
	}

	public void dumpNeededStates() {
		assert(recordNeededStates);
		for (String cam: cameraIds){
			states.get(cam).addTop();
			System.out.println(states.get(cam).size());
			states.get(cam).writeDown();
		}

	}
	@Override
	public String toString(){
		return "" + this.getClass().getSimpleName() + "_" + cameraIds.size() +
				"_" + utilityZoom + "_" + utilityUP + "_" + "_" + "_" + numberOfTimeStampsLookAhead;
	}
}
